#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>

#define NUM 1024
#define SIZE 32
#define SPACE " "

//保存完整的命令行
char cmd_line[NUM];
//保存打散之后的命令行字符串
char *g_argv[SIZE];


//shell运行原理
//通过让子进程执行命令，父进程等待&&解析命令
int main()
{
  //0.命令行解释器，一定是一个常驻内存的进程，不退出
  while(1)
  {
    //1.打印提示信息
    printf("[bai@localhost myshell]#");
    fflush(stdout);
    
    memset(cmd_line,'\0',sizeof(cmd_line));
    memset(g_argv,0,sizeof(g_argv));
    //2.获取用户的键盘输入[输入的是各种指令和选项："ls -a -l -i"]
    if(fgets(cmd_line,sizeof(cmd_line),stdin)==NULL)
    {
      continue;
    }
    cmd_line[strlen(cmd_line)-1]='\0';
    //printf("echo %s\n",cmd_line);
    //3.命令行字符串解析："ls -a -l -i" -> "ls" "-a" "-i"
    //我们可以编写程序通过空格分割，并通过字符指针指向
    //但C语言中的strtok()可以直接拆分，更方便
    g_argv[0]=strtok(cmd_line,SPACE);//第一次调用，要传入原始字符串
    int index = 1;
    if(strcmp(g_argv[0],"ls")==0)
    {
      g_argv[index++]="--color=auto";
    }
    if(strcmp(g_argv[0],"ll")==0)
    {
      g_argv[0]="ls";
      g_argv[index++]="-l";
      g_argv[index++]="--color=auto";

    }

    while(g_argv[index++]=strtok(NULL,SPACE));//第二次，如果还要解析原字符串，传入NULL
  
    //for debug
    //for(index=0;g_argv[index];index++)
    //  printf("g_argv[%d]: %s\n",index,g_argv[index]);
    //
    //4.内置命令，让父进程(shell)自己执行的命令，我们叫做内置命令、内建命令
    //内建命令本质其实就是shell中的一个函数调用
    if(strcmp(g_argv[0],"cd")==0)//如果是cd命令，则不用子进程执行，使用父进程执行
    {
      if(g_argv[1]!=NULL) chdir(g_argv[1]);//cd path
      continue;
    }
    //5.fork()
    pid_t id = fork();
    if(id==0)//子进程
    {
      printf("以下功能让子进程进行\n");
      execvp(g_argv[0],g_argv);
      exit(1);
    }
    //父进程
    int status=0;
    pid_t ret = waitpid(id,&status,0);
    if(ret>0)
    {
      printf("exit code: %d\n",WEXITSTATUS(status));
    }
  }
}
